package com.cyan.app.workflow.util.network;

import android.content.Context;

import com.cyan.app.workflow.AppData;
import com.cyan.app.workflow.common.HttpConfig;
import com.cyan.app.workflow.customview.ShowTipDialog;
import com.cyan.app.workflow.util.Logger;
import com.cyan.app.workflow.util.ParamUtils;
import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.Request.Method;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;

import java.util.Iterator;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.HashMap;


/**
 * 类名：VolleyUtil
 * 描述：volley封装工具类
 * @author GivenTang
 */
public class VolleyUtil {
    private volatile static VolleyUtil instance;
    private RequestQueue requestQueue;
    private static ShowTipDialog showdialog = new ShowTipDialog();
    private final static int TIME_OUT = 15000;
    private static boolean isShown = false;
    public static int TYPE_CONNECT_OUT = 0;
    public static int TYPE_CONNECT_ERROR = 1;
    public static int TYPE_CONNECT_OK = 2;
    private static int mConnectTimeout = 30 * 1000;
    private VolleyUtil() {

    }
    public static class ResponseStatus {
        public int mType;
        public int mCode;

        @Override
        public String toString() {
            return "ResponseStatus [mType=" + mType + ", mCode=" + mCode + "]";
        }
    };
    public void init(Context context) {
        requestQueue = Volley.newRequestQueue(context);
    }

    public RequestQueue getRequestQueue() {
        return requestQueue;
    }

    public static VolleyUtil getInstance() {
        if (null == instance) {
            synchronized (VolleyUtil.class) {
                if (null == instance) {
                    instance = new VolleyUtil();
                }
            }
        }
        return instance;
    }

    private static <T> void addRequest(RequestQueue requestQueue,
                                       Request<T> request, Object tag) {
        if (tag != null) {
            request.setTag(tag);
        }
        request.setShouldCache(false);
        request.setRetryPolicy(new DefaultRetryPolicy(TIME_OUT,
                DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
                DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        requestQueue.add(request);
    }

    public void cancelAllByTag(Object tag) {
        if (null != requestQueue) {
            if (tag != null) {
                requestQueue.cancelAll(tag);
            }
        }
    }

    public void cancelAll(Context context) {
        if (null != requestQueue) {
            requestQueue.cancelAll(context);
        }
    }

    /**
     * POST接口
     *
     * @param context
     * @param tag
     * @param params
     * @param clazz
     * @param listener
     * @param <T>
     */
    public static <T> void sendStringRequestByPost(Context context,
                                                   Object tag, Map<String, String> params, final Class<T> clazz,
                                                   final HttpListener<T> listener) {
        sendStringRequestByPost(context, HttpConfig.HTTP_MAIN_SERVER_URL,
                tag, params, clazz,
                listener);
    }

    public static <T> void sendStringRequestByPost(final Context context, String url,
                                                   Object tag, final Map<String, String> params, final Class<T> clazz,
                                                   final HttpListener<T> listener) {
        StringRequest stringRequest = new StringRequest(Method.POST, url,
                new Response.Listener<String>() {

                    @Override
                    public void onResponse(String s) {
                        Logger.e("onResponse Post", "s = " + s);
                        try {
                            if(clazz == null) {
                                listener.onResponse(s);
                            } else {
                                JSONTokener jsonParser = new JSONTokener(s);
                                JSONObject js = (JSONObject) jsonParser.nextValue();
                                int code = js.getInt("Code");
                                if (code >= 0) {
                                    T t = new Gson().fromJson(s, clazz);
                                    listener.onSuccess(t);
                                } else {
                                    listener.onCodeError(code, js.getString("Msg"));
                                }
                            }
                        } catch (Exception e) {
                            listener.onException(e);
                        }
                    }
                }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError volleyError) {
                listener.onVolleyError(volleyError);
            }
        }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return params == null ? super.getParams() : checkParams(ParamUtils.getRequestParamPost(params));
            }
        };
        addRequest(getInstance().getRequestQueue(), stringRequest, tag);
    }

    /**
     * 检查MAP中是否包含NULL值并替换为空
     *
     * @param map
     * @return
     */
    private static Map<String, String> checkParams(Map<String, String> map) {
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        String temp = "";
        while (it.hasNext()) {
            Map.Entry<String, String> pairs = (Map.Entry<String, String>) it.next();
            if (pairs.getValue() == null) {
                map.put(pairs.getKey(), "");
            }
            temp += pairs.getKey() + "=" + pairs.getValue() + "&";
        }
        Logger.e("checkParams", "url = " + HttpConfig.HTTP_MAIN_SERVER_URL + "?" + (temp.length() > 0 ? temp.substring(0, temp.length() - 1) : temp));
        return map;
    }

    /**
     * GET接口
     *
     * @param context
     * @param tag
     * @param params
     * @param clazz
     * @param listener
     * @param <T>
     */
    public static <T> void sendStringRequestByGet(Context context,
                                                  Object tag, Map<String, String> params,
                                                  final Class<T> clazz, final HttpListener<T> listener) {
        sendStringRequestByGet(context,
                HttpConfig.HTTP_MAIN_SERVER_URL, tag, params,
                clazz, listener);
    }

    public static <T> void sendStringRequestByGet(final Context context,
                                                  final String url, Object tag, final Map<String, String> params,
                                                  final Class<T> clazz, final HttpListener<T> listener) {
        StringRequest stringRequest = new StringRequest(Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {
                   //     showdialog.showDialog(context);
                        Logger.e("onResponse Get", "s = " + s);
                        try {
                            if(clazz == null) {
                                listener.onResponse(s);
                            } else {
                                JSONTokener jsonParser = new JSONTokener(s);
                                JSONObject js = (JSONObject) jsonParser.nextValue();
                                int code = js.getInt("Code");
                                if (code >= 0) {
                                    T t = new Gson().fromJson(s, clazz);//这里报错
                                    listener.onSuccess(t);
                                } else {
                                    listener.onCodeError(code, js.getString("Msg"));
                                }

                            }

                        } catch (Exception e) {
                            listener.onException(e);
                        } finally {
                         //   showdialog.closeDialog();
                        }
                    }
                }, new Response.ErrorListener() {

                        @Override
                        public void onErrorResponse(VolleyError volleyError) {
                          //  showdialog.closeDialog();
                            listener.onVolleyError(volleyError);
                        }
                    }
        )
        {
            @Override
            public String getUrl() {
                String sParams = ParamUtils.mapToStringParams(ParamUtils.getRequestParamGet(params));
            //    String sParams = ParamUtils.mapToStringParams(params);
                if (sParams.equals("")) {
                    return super.getUrl();
                } else {
                    Logger.e("getUrl", url + "?" + sParams);
                    return url + "?" + sParams;
                }
            }

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {

                Map<String, String> headers =  new HashMap<String, String>();
                headers.put("auth",  AppData.getInstance().getUserToken() );

                return headers;
            }
        };
        addRequest(getInstance().getRequestQueue(), stringRequest, tag);
    }

    /**
     * @param url
     *            需要访问的网址，不能为null
     * @return 服务端返回的字符串
     * @throws IOException
     */
    public static String get(String url, ResponseStatus responseObject) throws IOException {
        if (url == null || !NetworkUtil.isNetworkConnected()) {
            if (responseObject != null) {
                responseObject.mType = TYPE_CONNECT_OUT;
            }
            return null;
        }
        if (responseObject != null) {
            responseObject.mType = TYPE_CONNECT_ERROR;
        }
        HttpClient httpClient = getHttpClient();
        HttpGet httpGet = new HttpGet(url);
        InputStream inputStream = null;
        boolean succeed = false;
        try {
            httpGet.setHeader("Accept-Encoding", "gzip");
            HttpResponse response = httpClient.execute(httpGet);
            if (null == response) {
                return null;
            }

            int responseCode = response.getStatusLine().getStatusCode();
            if (responseObject != null) {
                responseObject.mType = TYPE_CONNECT_OK;
                responseObject.mCode = responseCode;
            }
            if (HttpURLConnection.HTTP_OK == responseCode) {
                HttpEntity httpEntity = response.getEntity();
                inputStream = httpEntity.getContent();

                boolean gzip = false;
                org.apache.http.Header contentEncoding = httpEntity.getContentEncoding();
                if (contentEncoding != null) {
                    String encoding = contentEncoding.getValue();
                    gzip = encoding.contains("gzip");
                }
                succeed = true;
                return readStream(inputStream, gzip);
            } else {
                return null;
            }
        } catch (ClientProtocolException e) {
            return null;
        } finally {
            closeStream(inputStream);

            if (!succeed) {
                httpGet.abort();
            }

            ClientConnectionManager connectionManager = httpClient.getConnectionManager();
            if (connectionManager != null) {
                connectionManager.shutdown();
            }
        }
    }
    static public HttpClient getHttpClient() {
        HttpParams params = new BasicHttpParams();
        ConnManagerParams.setTimeout(params, mConnectTimeout);

        HttpConnectionParams.setConnectionTimeout(params, mConnectTimeout);
        HttpConnectionParams.setSoTimeout(params, mConnectTimeout);

        ClientConnectionManager connectionManager = createClientConnectionManager(params);
        HttpClient httpClient = new DefaultHttpClient(connectionManager, params);
        return httpClient;
    }
    static private ClientConnectionManager createClientConnectionManager(HttpParams params) {
        SchemeRegistry registry = new SchemeRegistry();

        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        workAroundReverseDnsBugInHoneycombAndEarlier(registry);

        ClientConnectionManager connectionManager = new SingleClientConnManager(params, registry);
        return connectionManager;
    }
    private static void workAroundReverseDnsBugInHoneycombAndEarlier(SchemeRegistry registry) {
        /**
         * Android had a bug where HTTPS made reverse DNS lookups (fixed in Ice Cream Sandwich)
         * http://code.google.com/p/android/issues/detail?id=13117
         */
        LayeredSocketFactory socketFactory = new LayeredSocketFactory() {
            SSLSocketFactory delegate = SSLSocketFactory.getSocketFactory();

            @Override
            public Socket createSocket() throws IOException {
                return delegate.createSocket();
            }

            @Override
            public Socket connectSocket(Socket sock, String host, int port,
                                        InetAddress localAddress, int localPort,
                                        HttpParams params) throws IOException {
                return delegate.connectSocket(sock, host, port,
                        localAddress, localPort, params);
            }

            @Override
            public boolean isSecure(Socket sock) throws IllegalArgumentException {
                return delegate.isSecure(sock);
            }

            @Override
            public Socket createSocket(Socket socket, String host,
                                       int port, boolean autoClose) throws IOException {
                injectHostname(socket, host);
                return delegate.createSocket(socket, host, port, autoClose);
            }

            private void injectHostname(Socket socket, String host) {
                try {
                    Field field = InetAddress.class
                            .getDeclaredField("hostName");
                    field.setAccessible(true);
                    field.set(socket.getInetAddress(), host);
                } catch (Exception ignored) {
                }
            }
        };
        registry.register(new Scheme("https", socketFactory, 443));
    }
    private static void closeStream(Closeable close) {
        if (close == null) {
            return;
        }
        try {
            close.close();
        } catch (IOException e) {

        } finally {
            close = null;
        }
    }
    private static String readStream(InputStream input, boolean gzip) throws IOException {
        InputStream inputStream = null;
        if (gzip) {
            inputStream = new GZIPInputStream(input);
        } else {
            inputStream = input;
        }

        StringBuffer result = new StringBuffer();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        String str = null;
        while ((str = reader.readLine()) != null) {
            result.append(str).append("\n");
        }
        return result.toString();
    }
}